/**
 * Copyright 2018 人人开源 http://www.renren.io
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */

package com.tang.validator;


import com.tang.exception.RRException;

import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import java.util.Set;

/**
 * hibernate-validator校验工具类
 * <p>
 * 参考文档：http://docs.jboss.org/hibernate/validator/5.4/reference/en-US/html_single/
 *
 * @author tangyifei
 * @email 15062230055@163.com
 * @date 201803-15 10:50
 */
public class ValidatorUtils {

    private static Validator validator;

    /**
     * 一 般情况下,如果有些代码必须在项目启动的时候就执行,需要使用静态代码块,
     * 这种代码是主动执行的;需要在项目启动的时候就初始化,在不创建对象的情况下,
     * 其他程序来调用的时候,需要使用静态方法,这种代码是被动执行的.
     * 静态方法在类加载的时候就已经加载,可以用类名直接调用
     * 比如main方法就必须是静态的，这是程序入口
     * 两者的区别就是:静态代码块是自动执行的;静态方法是被调用的时候才执行的.
     * 静态代码块，在虚拟机加载类的时候就会加载执行，而且只执行一次;
     * 非静态代码块，在创建对象的时候（即new一个对象的时候）执行，每次创建对象都会执行一次
     * 相同点：都是在JVM加载类时且在构造方法执行之前执行，在类中都可以定义多个，
     * 　　　　一般在代码块中对一些static变量进行赋值。
     * 不同点：静态代码块在非静态代码块之前执行(静态代码块—>非静态代码块—>构造方法)。
     * 　　　　静态代码块只在第一次new执行一次，之后不再执行，而非静态代码块在每new
     * 　　　　一次就执行一次。非静态代码块可在普通方法中定义(不过作用不大)；而静态代码块不行。
     * 例：
     * 普通类public class PuTong {
     *        public PuTong(){
     *           System.out.print("默认构造方法！-->");
     *           }
     *           //非静态代码块
     *           {
     *             System.out.print("非静态代码块！-->");
     *           }
     *           //静态代码块
     *           static{
     *           System.out.print("静态代码块！-->");
     *           }
     *           public static void test(){
     *           {
     *            System.out.println("普通方法中的代码块！");
     *            }
     *          }
     *    }
     *    //测试类
     *    public class TestClass {
     *      // 区别两次new静态与非静态代码块执行情况
     *      public static void main(String[] args) {
     *      PuTong c1 = new PuTong();
     *      c1.test();
     *     PuTong c2 = new PuTong();
     *     c2.test();
     *     }
     *   }
     *   运行输出结果是：
     *      静态代码块！-->非静态代码块！-->默认构造方法！-->普通方法中的代码块！
     *      非静态代码块！-->默认构造方法！-->普通方法中的代码块！
     *  分析：对象的初始化顺序:首先执行父类静态的内容，
     *       父类静态的内容执行完毕后，接着去执行子类的静态的内容，
     *       当子类的静态内容执行完毕之后，再去看父类有没有非静态代码块，
     *       如果有就执行父类的非静态代码块，父类的非静态代码块执行完毕，
     *       接着执行父类的构造方法；父类的构造方法执行完毕之后，
     *       它接着去看子类有没有非静态代码块，如果有就执行子类的非静态代码块。
     *       子类的非静态代码块执行完毕再去执行子类的构造方法。
     *       总之一句话，静态代码块内容先执行，接着执行父类非静态代码块和构造方法，
     *       然后执行子类非静态代码块和构造方法。 
     *       而且子类的构造方法，不管这个构造方法带不带参数，
     *       默认的它都会先去寻找父类的不带参数的构造方法。
     *       如果父类没有不带参数的构造方法，
     *       那么子类必须用supper关键子来调用父类带参数的构造方法，否则编译不能通过。
     */
    static {
        validator = Validation.buildDefaultValidatorFactory().getValidator();
    }

    /**
     * 校验对象
     *
     * @param object 待校验对象
     * @param groups 待校验的组
     * @throws RRException 校验不通过，则报RRException异常
     */
    public static void validateEntity(Object object, Class<?>... groups)
            throws RRException {
        Set<ConstraintViolation<Object>> constraintViolations = validator.validate(object, groups);
        if (!constraintViolations.isEmpty()) {
            ConstraintViolation<Object> constraint = (ConstraintViolation<Object>) constraintViolations.iterator().next();
            throw new RRException(constraint.getMessage());
        }
    }
}
